<!--
Copyright 2014 The Chromium Authors. All rights reserved.
Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file.
-->

<link rel="import" href="../ct-failures.html">

<link rel="import" href="../../lib/network-simulator.html">
<link rel="import" href="../ct-commit-list.html">
<link rel="import" href="../ct-commit-log-mock.html">
<link rel="import" href="../ct-failure-group.html">
<link rel="import" href="../ct-step-failure.html">

<script>
(function () {
'use strict';

var assert = chai.assert;

describe('ct-failures', function() {
  describe('failureComparator', function() {
    it('should sort failures', function() {
      var analyzer = new CTFailures(new CTCommitList(undefined, []));

      var resultsByBuilder = {};
      var failure1 = new CTStepFailure("step1", "reason1", resultsByBuilder, 123, 123);
      var failure2 = new CTStepFailure("step1", "reason2", resultsByBuilder, 123, 123);
      var failure3 = new CTStepFailure("step1", "reason3", resultsByBuilder, 123, 123);
      var failure4 = new CTStepFailure("step2", "reason1", resultsByBuilder, 123, 123);

      var failures = [failure4, failure3, failure2, failure1];
      var expectedFailures = [failure1, failure2, failure3, failure4];
      assert.deepEqual(failures.sort(analyzer._failureComparator), expectedFailures);
    });
  });

  describe('failureByTreeListComparator', function() {
    it('should compare failures correctly', function() {
      var analyzer = new CTFailures(new CTCommitList(undefined, []));

      var cl1 = new CTCommitList(undefined, ['chromium:1', 'blink:2']);
      var cl2 = new CTCommitList(undefined, ['chromium:2', 'blink:1']);
      var cl3 = new CTCommitList(undefined, ['chromium:2', 'blink:2']);
      var cl4 = new CTCommitList(undefined, []);
      var group1 = new CTFailureGroup('', new CTStepFailureGroupData([], cl1));
      var group2 = new CTFailureGroup('', new CTStepFailureGroupData([], cl2));
      var group3 = new CTFailureGroup('', new CTStepFailureGroupData([], cl3));
      var group4 = new CTFailureGroup('', new CTStepFailureGroupData([], cl4));

      // Sort by last revision first.
      assert(analyzer._failureByTreeListComparator('chromium', group1, group2) > 0);
      assert(analyzer._failureByTreeListComparator('chromium', group2, group1) < 0);
      assert(analyzer._failureByTreeListComparator('chromium', group1, group1) == 0);

      // If the tree revisions are equal, take others.
      assert(analyzer._failureByTreeListComparator('chromium', group2, group3) > 0);

      // Prioritize the given tree.
      assert(analyzer._failureByTreeListComparator('chromium', group1, group2) > 0);
      assert(analyzer._failureByTreeListComparator('blink', group1, group2) < 0);

      // Default to 'chromium'.
      assert(analyzer._failureByTreeListComparator(undefined, group1, group2) > 0);

      // Failures without a revision go to the end.
      assert(analyzer._failureByTreeListComparator('chromium', group4, group1) > 0);
    });
  });

  describe('._mungeAlerts', function() {
    it('puts alerts into a standard format', function() {
      var analyzer = new CTFailures(new CTCommitList(undefined, []));
      var alerts = [
        {
          reason: 'reason',
          master_url: 'https://build.chromium.org/p/chromium.webkit',
          tree: 'blink',
        },
        {
          reason: 'reason:type',
          master_url: 'https://build.chromium.org/p/chromium.chrome',
          tree: 'chromium',
        },
        {
          master_url: 'https://build.chromium.org/p/chromium.gpu',
          tree: 'chromium',
        },
        {
          reason: 'reason:type',
          failureType: 'actual_type',
          tree: undefined,
        },
      ];
      analyzer._mungeAlerts(alerts);
      assert.lengthOf(alerts, 4);
      assert.deepEqual(alerts[0], {
        reason: 'reason',
        failureType: 'FAIL',
        master_url: 'https://build.chromium.org/p/chromium.webkit',
        tree: 'blink',
      });
      assert.deepEqual(alerts[1], {
        reason: 'reason',
        failureType: 'type',
        master_url: 'https://build.chromium.org/p/chromium.chrome',
        tree: 'chromium',
      });
      assert.deepEqual(alerts[2], {
        failureType: 'UNKNOWN',
        master_url: 'https://build.chromium.org/p/chromium.gpu',
        tree: 'chromium',
      });
      assert.deepEqual(alerts[3], {
        reason: 'reason:type',
        failureType: 'actual_type',
        tree: undefined,
      });
    });
  });

  describe('.groupFailuresByTreeAndReason', function() {
    it('groups failures', function() {
      var analyzer = new CTFailures(new CTCommitList(undefined, []));
      var linuxAnnotation = [1];
      var winAnnotation = [2];
      var blinkAnnotation = [3];
      var annotations = {
        'https_//build.chromium.org/p/chromium::Linux::1::unit_tests::MyTest': linuxAnnotation,
        'https_//build.chromium.org/p/chromium::Win::3::unit_tests::MyTest': winAnnotation,
        'https_//build.chromium.org/p/chromium.webkit::Linux::1::LayoutTests::MyTest': blinkAnnotation,
      };
      var failuresByTree = analyzer._groupFailuresByTreeAndReason([{
        tree: 'chromium',
        step_name: 'unit_tests',
        reason: 'MyTest',
        builder_name: 'Linux',
        failureType: 'a_type',
        failing_build: 1,
        last_failing_build: 2,
        master_url: 'https://build.chromium.org/p/chromium',
      }, {
        tree: 'chromium',
        step_name: 'unit_tests',
        reason: 'MyTest',
        builder_name: 'Win',
        failureType: 'another_type',
        failing_build: 3,
        last_failing_build: 44,
        master_url: 'https://build.chromium.org/p/chromium',
      }, {
        tree: 'blink',
        step_name: 'LayoutTests',
        reason: 'MyTest',
        builder_name: 'Linux',
        failureType: 'a_type',
        failing_build: 1,
        last_failing_build: 2,
        master_url: 'https://build.chromium.org/p/chromium.webkit',
      }], annotations);
      assert.equal(Object.size(failuresByTree), 2);
      assert.equal(Object.size(failuresByTree.chromium), 1);
      assert.property(failuresByTree.chromium, '{"step":"unit_tests","reason":"MyTest"}');
      var chromiumBuilders = failuresByTree.chromium['{"step":"unit_tests","reason":"MyTest"}'];
      assert.equal(Object.size(chromiumBuilders), 2);
      assert.equal(Object.size(chromiumBuilders.Linux), 7);
      assert.propertyVal(chromiumBuilders.Linux, 'key', 'https_//build.chromium.org/p/chromium::Linux::1::unit_tests::MyTest');
      assert.propertyVal(chromiumBuilders.Linux, 'actual', 'a_type');
      assert.propertyVal(chromiumBuilders.Linux, 'lastFailingBuild', 2);
      assert.propertyVal(chromiumBuilders.Linux, 'masterUrl', 'https://build.chromium.org/p/chromium');
      assert.propertyVal(chromiumBuilders.Linux, 'failingBuildCount', 2);
      assert.propertyVal(chromiumBuilders.Linux, 'annotation', linuxAnnotation);

      assert.equal(Object.size(chromiumBuilders.Win), 7);
      assert.propertyVal(chromiumBuilders.Win, 'key', 'https_//build.chromium.org/p/chromium::Win::3::unit_tests::MyTest');
      assert.propertyVal(chromiumBuilders.Win, 'actual', 'another_type');
      assert.propertyVal(chromiumBuilders.Win, 'lastFailingBuild', 44);
      assert.propertyVal(chromiumBuilders.Win, 'masterUrl', 'https://build.chromium.org/p/chromium');
      assert.propertyVal(chromiumBuilders.Win, 'failingBuildCount', 42);
      assert.propertyVal(chromiumBuilders.Win, 'annotation', winAnnotation);

      assert.equal(Object.size(failuresByTree.blink), 1);
      assert.property(failuresByTree.blink, '{"step":"LayoutTests","reason":"MyTest"}');
      var blinkBuilders = failuresByTree.blink['{"step":"LayoutTests","reason":"MyTest"}'];
      assert.equal(Object.size(blinkBuilders), 1);
      assert.equal(Object.size(blinkBuilders.Linux), 7);
      assert.propertyVal(blinkBuilders.Linux, 'key', 'https_//build.chromium.org/p/chromium.webkit::Linux::1::LayoutTests::MyTest');
      assert.propertyVal(blinkBuilders.Linux, 'actual', 'a_type');
      assert.propertyVal(blinkBuilders.Linux, 'lastFailingBuild', 2);
      assert.propertyVal(blinkBuilders.Linux, 'masterUrl', 'https://build.chromium.org/p/chromium.webkit');
      assert.propertyVal(blinkBuilders.Linux, 'failingBuildCount', 2);
      assert.propertyVal(blinkBuilders.Linux, 'annotation', blinkAnnotation);
    });
  });

  describe('.processAnalysisResultsForStepFailureGroup', function() {
    var analyzer;
    var failures;

    beforeEach(function() {
      analyzer = new CTFailures(new CTCommitList(undefined, []));
      failures = [];
      failures.push(
        new CTStepFailure('unit_tests', 'suite1.test1', {
          'Linux': {
            'key': 'failure_key1',
            'actual': 'failure_type1',
            'lastFailingBuild': 5,
            'masterUrl': 'https://build.chromium.org/p/chromium',
            'failingBuildCount': 1,
            'annotation': null,
            'isTreeCloser': false,
          }
        })
      );
    });

    it('should return an empty object if analysis result is not available', function() {
      var analysisResults = undefined;
      var suspectedCLsByRepo = analyzer._processAnalysisResultsForStepFailureGroup(failures, analysisResults);
      assert.deepEqual(suspectedCLsByRepo, {});
    });

    it('should return matched analysis results', function() {
      var analysisResults = [
        {
          'master_url': 'https://build.chromium.org/p/chromium',
          'builder_name': 'Linux',
          'build_number': 5,
          'step_name': 'unit_tests',
          'is_sub_test': false,
          'first_known_failed_build_number': 3,
          'suspected_cls': [
            {
              'revision': 'git_hash3',
              'commit_position': 3,
              'repo_name': 'chromium'
            },
            {
              'revision': 'git_hash5',
              'commit_position': 5,
              'repo_name': 'v8'
            }
          ]
        },
        {
          'master_url': 'https://build.chromium.org/p/chromium',
          'builder_name': 'Linux',
          'build_number': 5,
          'step_name': 'browser_tests',
          'is_sub_test': false,
          'first_known_failed_build_number': 2,
          'suspected_cls': [
            {
              'revision': 'git_hash2',
              'commit_position': 2,
              'repo_name': 'chromium'
            }
          ]
        },
      ];

      var suspectedCLsByRepo = analyzer._processAnalysisResultsForStepFailureGroup(failures, analysisResults);

      assert.deepEqual(suspectedCLsByRepo, {
        'chromium': [
          {
            'commitPosition': 3,
            'revision': 'git_hash3',
            'builds': ['https://build.chromium.org/p/chromium/builders/Linux/builds/5']
          },
        ],
        'v8': [
          {
            'commitPosition': 5,
            'revision': 'git_hash5',
            'builds': ['https://build.chromium.org/p/chromium/builders/Linux/builds/5']
          },
        ]
      });
    });
  });

  describe('.update', function() {
    it('should update everything', function(done) {
      var simulator = new NetworkSimulator(assert, done);
      var netData = {
        'sheriff-o-matic.appspot.com/alerts': {
          stale_builder_alerts: [
            {
              'master_url': 'http://build.chromium.org/p/chromium.webkit',
              'builder_name': 'Linux Tests',
              'state': 'building',
              'last_update_time': 1409697347.089103,
              'pending_builds': 4,
              'tree': 'blink',
            },
            {
              'master_url': 'http://build.chromium.org/p/chromium.linux',
              'builder_name': 'Linux Tests (dbg)',
              'state': 'offline',
              'last_update_time': 1409697347.089103,
              'pending_builds': 2,
              'tree': 'blink',
            },
            {
              'master_url': 'http://build.chromium.org/p/chromium.win',
              'builder_name': 'Win Tests (dbg)',
              'state': 'idle',
              'last_update_time': 1409697347.089103,
              'pending_builds': 5,
              'tree': 'blink',
            },
            {
              'last_update_time': 1409697347.089103,
              'master_url': 'https://build.chromium.org/p/chromium.perf',
              'master_name': 'chromium.perf',
              'tree': 'chromium',
            },
          ],
          range_groups: [
            {
              sort_key: 'linux: Linux Tests (dbg)(1)',
              failure_keys: [ 'f1', ],
              likely_revisions: [
                'chromium: 100',
                'chromium: 101',
              ],
              merged_first_failing: {
                blink: 50,
                nacl: 50,
                v8: 50,
                chromium: 101,
              },
              merged_last_passing: {
                blink: 50,
                nacl: 50,
                v8: 50,
                chromium: 99,
              },
            },
            {
              sort_key: 'linux: Linux Tests (dbg)(1)',
              failure_keys: [ 'f2', ],
              likely_revisions: [
                'chromium: 200',
              ],
              merged_first_failing: {
                blink: 50,
                nacl: 50,
                v8: 50,
                chromium: 200,
              },
              merged_last_passing: {
                blink: 50,
                nacl: 50,
                v8: 50,
                chromium: 199,
              },
            },
            {
              sort_key: 'mac: Mac Tests',
              failure_keys: [ 'f3', ],
              likely_revisions: [
                'chromium: 100',
                'chromium: 101',
              ],
              merged_first_failing: {
                blink: 50,
                nacl: 50,
                v8: 50,
                chromium: 200,
              },
              merged_last_passing: {
                blink: 50,
                nacl: 50,
                v8: 50,
                chromium: 199,
              },
            },
          ],
          alerts: [
            {
              last_result_time: 1409697347.089103,
              passing_build: 2485,
              last_failing_build: 2489,
              failing_build: 2486,
              latest_revisions: {
                v8: 50,
                chromium: 103,
                nacl: 50,
                blink: 51
              },
              master_url: "https://build.chromium.org/p/chromium.linux",
              reason: null,
              failing_revisions: {
                v8: 50,
                chromium: 101,
                nacl: 50,
                blink: 50
              },
              builder_name: "Linux Tests (dbg)(1)",
              key: "f1",
              step_name: "compile",
              tree: 'chromium',
              passing_revisions: {
                v8: 50,
                chromium: 99,
                nacl: 50,
                blink: 50
              },
              would_close_tree: true
            },
            {
              last_result_time: 1409697347.089103,
              passing_build: 2485,
              last_failing_build: 2489,
              failing_build: 2486,
              latest_revisions: {
                v8: 50,
                chromium: 203,
                nacl: 50,
                blink: 51
              },
              master_url: "https://build.chromium.org/p/chromium.linux",
              reason: 'Foo.Test',
              failing_revisions: {
                v8: 50,
                chromium: 200,
                nacl: 50,
                blink: 50
              },
              builder_name: "Linux Tests (dbg)(1)",
              key: "f2",
              step_name: "foo_tests",
              tree: 'chromium',
              passing_revisions: {
                v8: 50,
                chromium: 99,
                nacl: 50,
                blink: 50
              },
              would_close_tree: true
            },
            {
              last_result_time: 1409697347.089103,
              passing_build: 2485,
              last_failing_build: 2489,
              failing_build: 2486,
              latest_revisions: {
                v8: 50,
                chromium: 103,
                nacl: 50,
                blink: 51
              },
              master_url: "https://build.chromium.org/p/chromium.mac",
              reason: null,
              failing_revisions: {
                v8: 50,
                chromium: 101,
                nacl: 50,
                blink: 50
              },
              builder_name: "Mac Tests",
              key: "f3",
              step_name: "compile",
              tree: 'chromium',
              passing_revisions: {
                v8: 50,
                chromium: 99,
                nacl: 50,
                blink: 50
              },
              would_close_tree: true
            },
          ],
          date: 1409697816.726562,
        },
        'trooper-o-matic.appspot.com/alerts': {

        }
      }
      simulator.json = function(url) {
        var matched = Object.find(netData, function(key) {
          return url.indexOf(key) != -1;
        });
        if (matched) {
          return Promise.resolve(netData[matched]);
        } else {
          return Promise.reject('Unexpected url: ' + url);
        }
      };
      simulator.runTest(function() {
        var analyzer = new CTFailures(new CTRepositories());
        return analyzer.update().then(function() {
          assert.property(analyzer.failures, 'chromium');
          assert.lengthOf(analyzer.failures.chromium, 4);
          var masterFailureGroup = analyzer.failures.chromium[0];
          var testFailureGroup = analyzer.failures.chromium[1];
          var compileFailureGroup = analyzer.failures.chromium[2];
          var macCompileFailureGroup = analyzer.failures.chromium[3];
          assert.equal(compileFailureGroup.constructor, CTFailureGroup);
          assert.equal(compileFailureGroup.key, 'compile::Linux Tests (dbg)(1)');
          assert.lengthOf(compileFailureGroup.data.failures, 1);
          var failure = compileFailureGroup.data.failures[0];
          assert.equal(failure.constructor, CTStepFailure);
          assert.equal(failure.key, 'compile::Linux Tests (dbg)(1)');
          assert.equal(failure.step, 'compile');
          assert.equal(failure.testName, null);
          var resultNodesByBuilder = failure.resultNodesByBuilder;
          assert.property(resultNodesByBuilder, 'Linux Tests (dbg)(1)');
          var dbgBuilder = resultNodesByBuilder['Linux Tests (dbg)(1)'];
          assert.propertyVal(dbgBuilder, 'actual', 'UNKNOWN');
          assert.propertyVal(dbgBuilder, 'lastFailingBuild', 2489);
          assert.propertyVal(dbgBuilder, 'masterUrl', 'https://build.chromium.org/p/chromium.linux');
          assert.propertyVal(dbgBuilder, 'failingBuildCount', 4);
          assert.property(analyzer.failures, 'blink');
          assert.lengthOf(analyzer.failures.blink, 3);
          assert.equal(analyzer.failures.blink[0].data.failure.hoursSinceLastUpdate, 0.13);

          var commitList = compileFailureGroup.data.commitList;
          assert.equal(commitList.constructor, CTCommitList);
          assert.lengthOf(commitList.repositories, 1);
          var repositoryCommitList = commitList.repositories[0];
          assert.propertyVal(repositoryCommitList, 'range', '100 : 101');
          assert.propertyVal(repositoryCommitList, 'expanded', false);

          // Flip |expanded| to true to check that it's preserved across updates.
          repositoryCommitList.expanded = true;

          // Update |netData| to make sure it's propagated into the updated values.
          netData['sheriff-o-matic.appspot.com/alerts'].alerts[0].last_failing_build = 2490;

          return analyzer.update().then(function() {
            assert.strictEqual(analyzer.failures.chromium[0], masterFailureGroup)
            assert.strictEqual(analyzer.failures.chromium[1], testFailureGroup)
            assert.strictEqual(analyzer.failures.chromium[2], compileFailureGroup)
            assert.strictEqual(analyzer.failures.chromium[3], macCompileFailureGroup)
            assert.strictEqual(compileFailureGroup.data.failures[0], failure)
            assert.strictEqual(failure.resultNodesByBuilder, resultNodesByBuilder);
            assert.strictEqual(resultNodesByBuilder['Linux Tests (dbg)(1)'], dbgBuilder);
            assert.propertyVal(dbgBuilder, 'lastFailingBuild', 2490);

            assert.strictEqual(compileFailureGroup.data.commitList, commitList);
            assert.strictEqual(commitList.repositories[0], repositoryCommitList);
            assert.propertyVal(repositoryCommitList, 'expanded', true);
          });
        });
      });
    });
  });

  describe('processTrooperFailures', function() {
    it('should filter out updates that are not alerts', function() {
      var analyzer = new CTFailures(new CTCommitList(undefined, []));
      analyzer.failures = {};
      analyzer._processTrooperFailures({},
          {'key_1': {'tree_1': {should_alert: true, should_appear: true},
                     'tree_2': {should_alert: false, should_appear: false}},
           'key_2': {'tree_3': {should_alert: false, should_appear: false},
                     'tree_4': {should_alert: true, should_appear: true}}});
      analyzer.failures['trooper'].forEach(function(failure) {
        assert(failure.data.data.should_appear);
      });
      assert(analyzer.failures['health'].length == 2);
    });
  });

  describe('processTrooperFailures', function() {
    it('should list all failures with category builder and master', function() {
      var emptyCommit = new CTCommitList(undefined, []);
      var emptyGroupData = new CTStepFailureGroupData([], emptyCommit);
      var group1 = new CTFailureGroup('', emptyGroupData, 'builder');
      var group2 = new CTFailureGroup('', emptyGroupData, 'failedOnce');
      var group3 = new CTFailureGroup('', emptyGroupData, 'master');

      var analyzer = new CTFailures(new CTCommitList(undefined, []));
      analyzer.failures = {};
      analyzer._processTrooperFailures(
          {'chromium': [group1], 'blink': [group2, group3], 'chromium.perf': [group3]}, {});
      analyzer.failures['trooper'].forEach(function(failure) {
        assert(failure.category == 'builder' || failure.category == 'master');
      });
      assert(analyzer.failures['trooper-fyi'].length == 0);
      assert.equal(analyzer.failures['trooper'].length, 3);
    });
  });

  describe('processTrooperFYIFailures', function() {
    it('should list non-chromium or blink failures in trooper-fyi', function() {
      var emptyCommit = new CTCommitList(undefined, []);
      var emptyGroupData = new CTStepFailureGroupData([], emptyCommit);
      var group1 = new CTFailureGroup('', emptyGroupData, 'builder');
      var group2 = new CTFailureGroup('', emptyGroupData, 'failedOnce');
      var group3 = new CTFailureGroup('', emptyGroupData, 'master');

      var analyzer = new CTFailures(new CTCommitList(undefined, []));
      analyzer.failures = {};
      analyzer._processTrooperFailures(
          {'tree_1': [group1], 'tree_2': [group2, group3]}, {});
      analyzer.failures['trooper-fyi'].forEach(function(failure) {
        assert(failure.category == 'builder' || failure.category == 'master');
      });
      assert(analyzer.failures['trooper-fyi'].length == 2);
      assert(analyzer.failures['trooper'].length == 0);
    });
  });

  describe('processFailuresForRangeGroup', function() {
    it('should use tree name from json', function() {
      var analyzer = new CTFailures(null);

      var annotations = Promise.resolve({
        'test-key': 'test-annotation',
      })
      var range_groups = {
        'failure_keys': ['test-key'],
        'likely_revisions': []
      }
      var alertsByKey = {
          'test-key': {
              'key': 'test-key',
              'step_name': 'bot_update',
              'tree': 'test-tree',
              'builder_name': 'Linux',
              'last_failing_build': 1234,
              'failing_build': 1234,
              'master_url': 'https://build.chromium.org/p/chromium',
              'reason': 'test-reason',
          }
      }
      var newFailures = {}

      analyzer._processFailuresForRangeGroup(newFailures, range_groups, alertsByKey, annotations)
      assert.ok('test-tree' in newFailures)
      assert.notOk('chromium' in newFailures)
    });
  });

});

})()
</script>
